package com.wontube.smr.bo.sites.youtube.feed;

import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.XPathException;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;

import com.wontube.client.extension.utils.DateUtils;
import com.wontube.client.extension.utils.StringUtils;
import com.wontube.client.extension.utils.net.URLUtils;
import com.wontube.client.modle.vo.PagenateInfo;
import com.wontube.core.pub.AppEnvironment;
import com.wontube.smr.bo.constants.SiteConstants;
import com.wontube.smr.bo.constants.StandarAreas;
import com.wontube.smr.client.constants.MovieGenres;
import com.wontube.smr.client.model.po.Movie;
import com.wontube.smr.client.model.vo.MovieItems;

/**
 * youtube feed的查询与搜索
 * 
 * @author tanggf(2012-3-27)
 * 
 */
public final class YouTubeFeedsBO {
	private final static Logger logger = LoggerFactory.getLogger(YouTubeFeedsBO.class);
	
	/** youtube 标准feed的基本url */
	private final static String standardFeedsFormatUrl = "http://gdata.youtube.com/feeds/api/standardfeeds/{}/?v=2&alt=rss&max-results={}&start-index={}";

	/** youtube视频短片搜索url */
	private static final String videoFeedUrlFormat = "http://gdata.youtube.com/feeds/api/videos?v=2&alt=rss&max-results={}&start-index={}";

	// 单视频查询地址
	private static final String singleVideoFormatUrl = "http://gdata.youtube.com/feeds/api/videos/{}?v=2&alt=rss";
	
	// 相关视频地址
	private static final String relatedVideoUrlFormat = "http://gdata.youtube.com/feeds/api/videos/{}/related?v=2&alt=rss&max-results={}&start-index={}";
	
	private static final SimpleDateFormat airDateFormat = new SimpleDateFormat("yyyy-MM-dd");	
	
	private YouTubeFeedsBO() {
	}
	
	public static final Movie getSingle(final String videoId) {
		String url = StringUtils.format(singleVideoFormatUrl, videoId);
		return getSingleVideo(url);		
	}
	
	public static final MovieItems listRelated(final String videoId, final PagenateInfo pageInfo) {
		String url = StringUtils.format(relatedVideoUrlFormat, videoId, pageInfo.getPageSize(), pageInfo.getStart());
		MovieItems items = searchWithUrl(url);
		items.SetPageInfo(pageInfo);
		return items;		
	}

	/**
	 * 查询youtube标准feed
	 * 
	 * @param params
	 * @return
	 * @throws DocumentException 
	 * @throws IOException
	 */
	public static MovieItems selectStandardFeeds(String feedId, String period, String category, PagenateInfo pageInfo)  {
		String url = StringUtils.format(standardFeedsFormatUrl, feedId, pageInfo.getPageSize(), pageInfo.getStart());
		if ( ! StringUtils.isNullOrEmpty(period)) {
			url += "&time=" + period;
		}
		if ( ! StringUtils.isNullOrEmpty(category)) {
			url += "&category=" + category;
		}
		MovieItems items = searchWithUrl(url);
		items.SetPageInfo(pageInfo);
		return items;
	}

	/**
	 * 搜索youtube视频
	 * 
	 * @param params
	 * @return
	 * @throws DocumentException 
	 * @throws IOException
	 */
	public static MovieItems searchVideoFeeds(String query, String category, PagenateInfo pageInfo) {		
		String url = StringUtils.format(videoFeedUrlFormat, pageInfo.getPageSize(), pageInfo.getStart());
		if ( ! StringUtils.isNullOrEmpty(query)) {
			url += "&q=" + URLUtils.encodeURIComponent("\"" + query + "\"");
		}
		if ( ! StringUtils.isNullOrEmpty(category)) {
			url += "&category=" + category;
		}
		MovieItems items = searchWithUrl(url);
		items.SetPageInfo(pageInfo);
		return items;
	}

	public static final MovieItems searchWithUrl(final String url) {
		MovieItems movieItems = new MovieItems();		
		InputStreamReader isReader = null;
		HttpURLConnection conn = null;
		try {			
			conn = (HttpURLConnection) new URL(url).openConnection();
			conn.connect();
			int code = conn.getResponseCode();
			if (code != HttpURLConnection.HTTP_OK) {
				return new MovieItems();
			}
			
			isReader = new InputStreamReader(conn.getInputStream(), AppEnvironment.charsetName);
			InputSource inputSource = new InputSource(isReader);

			SAXReader saxReader = new SAXReader();
			Document doc = saxReader.read(inputSource);
			Node node = doc.selectSingleNode("/rss/channel/openSearch:totalResults");
			String value = node == null ? "" : node.getText();
			int totalResults = Integer.parseInt(value);
			movieItems.setTotalCount(totalResults);
			
			@SuppressWarnings("unchecked")
			List<Element> items = doc.selectNodes("/rss/channel/item");
			int count = 0;
			for (Element item : items) {
				Movie movie = toMovieItem(item);
				if (movie == null) {
					continue;
				}
				
				movie.setSortIndex(++count);
				movieItems.getItems().add(movie);
			}
		} catch(Exception ex) {
			logger.error("errors happened when fetched url from youtube: ", ex.toString());
		} finally {
			try {
				if (isReader != null) {
					isReader.close();
				}
				if (conn != null) {
					conn.disconnect();
				}
			} catch (Exception e) {				
			}			
		}
		
		return movieItems;
	}
	
	public static final Movie getSingleVideo(final String url){				
		InputStreamReader isReader = null;		
		try {
			isReader = new InputStreamReader(new URL(url).openStream(), AppEnvironment.charsetName);
			InputSource inputSource = new InputSource(isReader);
			SAXReader saxReader = new SAXReader();
			Document doc = saxReader.read(inputSource);
			Element item = (Element) doc.selectSingleNode("item");
			
			return toMovieItem(item);
		} catch (Exception e) {
			logger.warn("can not fetch {} from YouTube: {}", url, e.toString());
		} finally {
			try {
				if(isReader != null){
					isReader.close();
				}
			} catch (Exception e2) {				
			}		
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	private static Movie toMovieItem(Element item) {
		Node node = item.selectSingleNode("media:group/yt:videoid");
		String videoId = node == null ? "" : node.getText();
		if ("".equals(videoId)) {
			return null;
		}
		
		Movie movie = new Movie();	
		movie.setWebsite(SiteConstants.WEBSITE_YOUTUBE);
		movie.setArea(StandarAreas.DEFAULT);		
		
		movie.setId(SiteConstants.ID_PREFIX_YOUTUBE + videoId);
		movie.setVideoId(videoId);
		movie.setItemUrl("http://www.youtube.com/watch?v=" + videoId);
		
		node = item.selectSingleNode("title");
		movie.setTitle(node == null ? "" : node.getText());
		node = item.selectSingleNode("author");
		movie.setUploader(node == null ? "" : node.getText());
		node = item.selectSingleNode("yt:accessControl[@action='embed']");
		
		String value = node == null ? "allowed" : ((Element)node).attributeValue("permission", "denied");
		movie.setAllowEmbed("allowed".equalsIgnoreCase(value) ? 1 : 0);
		node = item.selectSingleNode("yt:firstReleased");
		if (node != null) {
			value = node.getText();
			Date date = DateUtils.parse(airDateFormat, value);
			movie.setPublishTime(date);
		}
		List<Element> listNode = item.selectNodes("media:group/media:category[@scheme='http://gdata.youtube.com/schemas/2007/moviegenres.cat']");
		StringBuilder sb = new StringBuilder();
		if (listNode.size() > 0) { // Movie
			movie.setMovieType(Movie.TYPE_MOVIE);
			for (Element ele : listNode) {
				value = node == null ? "" : ele.getText();
				if (!"".equals(value)) {
					int genreValue = Integer.parseInt(value);					
					String genreName = MovieGenres.getGenreName(genreValue);
					
					if (!"".equals(genreName)) {
						if (sb.length() > 0) {
							sb.append(", ");
						}
						sb.append(genreName);
					}				
				}
			}
			movie.setGenre(sb.toString());
		} else { 
			node = item.selectSingleNode("category[@domain='http://gdata.youtube.com/schemas/2007/categories.cat']");
			value = node == null ? "" : node.getText();
			if ("Music".equals(value)) {
				movie.setMovieType(Movie.TYPE_MUSIC);
			} else {
				movie.setMovieType("Video");
				movie.setGenre(value);
			}
		}
		sb.setLength(0);
		
		listNode = item.selectNodes("media:group/media:credit[@role='Director']");
		for (Element ele : listNode) {
			if (sb.length() > 0) {
				sb.append(", ");
			}
			sb.append(ele.getText());
		}
		movie.setDirector(sb.toString());
		
		sb.setLength(0);
		listNode = item.selectNodes("media:group/media:credit[@role='Actor']");
		for (Element ele : listNode) {
			if (sb.length() > 0) {
				sb.append(", ");
			}
			sb.append(ele.getText());
		}
		movie.setActors(sb.toString());
		
		node = item.selectSingleNode("media:group/media:description");
		movie.setDescription(node == null ? "" : node.getText());
		node = item.selectSingleNode("media:group/media:thumbnail[@yt:name='default']");
		movie.setThumbnail(node == null ? "" : ((Element)(node)).attributeValue("url"));
		node = item.selectSingleNode("media:group/media:thumbnail[@yt:name='poster']");
		if (node != null) {
			String banner = ((Element)node).attributeValue("url");
			movie.setBannerImgUrl(node == null ? "" : banner);
		}
		
		node = item.selectSingleNode("media:group/yt:duration");
		if (node != null) {
			value = ((Element)node).attributeValue("seconds", "0");
			movie.setDuration(Integer.parseInt(value));
		}
		
		try {
			node = item.selectSingleNode("gd:rating");
			if (node != null) {
				value = ((Element)node).attributeValue("average", "0");
				movie.setScore(BigDecimal.valueOf(Double.parseDouble(value)));
			}
		} catch (XPathException e) {
		}
		
		node = item.selectSingleNode("yt:statistics");
		if (node != null) {
			value = ((Element)node).attributeValue("viewCount", "0");
			movie.setViewCount(Integer.parseInt(value));
		}

		return movie; 
	}
}
